package com.zyh.scs.admin.service.jd;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zyh.scs.admin.api.bo.JDCashierDto;
import com.zyh.scs.admin.api.bo.JDCashierOrderPayDto;
import com.zyh.scs.admin.api.bo.PayResVo;
import com.zyh.scs.admin.api.constant.TransactionTypeEnum;
import com.zyh.scs.admin.api.constant.UserStateEnum;
import com.zyh.scs.admin.api.constant.open.CodeSet;
import com.zyh.scs.admin.api.constant.open.OrderStatus;
import com.zyh.scs.admin.api.dto.jd.PayConfirmParam;
import com.zyh.scs.admin.api.entity.SysUser;
import com.zyh.scs.admin.api.entity.UserOrderInfo;
import com.zyh.scs.admin.api.entity.UserPayment;
import com.zyh.scs.admin.api.entity.UserTransaction;
import com.zyh.scs.admin.api.utils.UserTypeToNameUtil;
import com.zyh.scs.admin.base.jd.JdResultParam;
import com.zyh.scs.admin.base.open.LaoSheResponse;
import com.zyh.scs.admin.mapper.UserOrderInfoMapper;
import com.zyh.scs.admin.service.SysUserService;
import com.zyh.scs.admin.service.UserOrderInfoService;
import com.zyh.scs.admin.service.UserPaymentService;
import com.zyh.scs.admin.service.UserTransactionService;
import com.zyh.scs.admin.utils.jd.AutologinParamUtil;
import com.zyh.scs.admin.utils.jd.HttpUtils;
import com.zyh.scs.admin.utils.jd.PayConfirmParamUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * 京东锦礼-收银台
 */
@Slf4j
@Service
public class JDCashierServiceImpl implements JDCashierService {

    private static final String TRACE_ID = "traceId";
    @Value("${biz.jd.bpin}")
    public String BPIN;
    @Value("${biz.jd.accessKey}")
    public String ACCESS_KEY;
    @Value("${biz.jd.rsaSignPrivateKey}")
    public String RSA_SIGN_PRIVATE_KEY;
    @Value("${biz.jd.rsaEncryptPublicKey}")
    public String RSA_ENCRYPT_PUBLIC_KEY;

    /*@Resource
    OrderTransactionFlowMapper transactionFlowMapper;*/
    @Value("${biz.jd.merchantId}")
    public String JD_MERCHANT_ID;
    @Value("${biz.jd.merchantName}")
    public String JD_MERCHANT_NAME;
    @Value("${biz.jd.cashierUrl}")
    public String JD_CASHIER_URL;
    @Value("${biz.jd.confirmOrderUrl}")
    public String JD_confirm_URL;
    @Resource
    SysUserService sysUserService;
    @Resource
    UserOrderInfoMapper userOrderInfoMapper;
    @Resource
    RedissonClient redissonClient;

    @Resource
    UserOrderInfoService userOrderInfoService;

    @Resource
    UserTransactionService userTransactionService;

    @Resource
    UserPaymentService userPaymentService;

    /**
     * 接收下单通知（跳转商户收银台）
     *
     * @param dto
     * @param request
     * @param response
     * @return
     */
    @Override
    public LaoSheResponse<String> cashier(JDCashierDto dto, HttpServletRequest request, HttpServletResponse response) {

        log.info("下单信息dto:{}", dto);
        // 获取下单信息
        String jdOrderId = AutologinParamUtil.decryptParam(dto.getOrderId(), RSA_SIGN_PRIVATE_KEY);
        log.info("下单信息jdOrderId:{}", jdOrderId);
        String submitOrderTime = AutologinParamUtil.decryptParam(dto.getSubmitOrderTime(), RSA_SIGN_PRIVATE_KEY); // 下单时间，Unix时间戳
        log.info("下单信息submitOrderTime:{}", submitOrderTime);
        String uid = AutologinParamUtil.decryptParam(dto.getUid(), RSA_SIGN_PRIVATE_KEY); // 用户ID
        log.info("下单信息uid:{}", uid);
        String totalMoney = AutologinParamUtil.decryptParam(dto.getTotalMoney(), RSA_SIGN_PRIVATE_KEY); // 订单总金额，保留两位小数，可直接用于支付
        log.info("下单信息totalMoney:{}", totalMoney);
        String freight = AutologinParamUtil.decryptParam(dto.getFreight(), RSA_SIGN_PRIVATE_KEY); // 订单运费
        log.info("下单信息freight:{}", freight);

        log.info("接收京东下单通知，京东订单号:{},用户工号：{}，订单总价{}，运费{}", jdOrderId, uid, totalMoney, freight);
        List<SysUser> sysUserList = sysUserService.lambdaQuery().eq(SysUser::getUserJobNumber, uid).list();

        if (null == sysUserList) {
            log.error("接收下单通知（跳转商户收银台） >>> 用户不存在， uid: {}", uid);
            return LaoSheResponse.error(CodeSet.JD_USER_NULL, "");
        }

        if (sysUserList.size() > 1) {
            log.error("接收下单通知（跳转商户收银台） >>> 存在多条用户， uid: {}", uid);
            return LaoSheResponse.error(CodeSet.JD_USER_MUCH, "");
        }

        final SysUser one = sysUserList.stream().findAny().orElse(null);

        assert one != null;
        if (UserStateEnum.LOCK.getCode().equals(one.getLockFlag())) {
            log.error("接收下单通知（跳转商户收银台） >>> 该账户被冻结， uid: {}", uid);
            return LaoSheResponse.error(CodeSet.JD_USER_LOCK, "");
        }

        if ("2".equals(one.getCardState())) {
            log.error("接收下单通知（跳转商户收银台） >>> 该账户餐卡账户被冻结， uid: {}", uid);
            return LaoSheResponse.error(CodeSet.JD_USER_CARD_LOCK, "");
        }

        if ("2".equals(one.getCashState())) {
            log.error("接收下单通知（跳转商户收银台） >>> 该账户现金账户被冻结， uid: {}", uid);
            return LaoSheResponse.error(CodeSet.JD_USER_CASH_LOCK, "");
        }


        //userOrderInfo
        final UserOrderInfo userOrderInfo = userOrderInfoMapper.selectOne(Wrappers.<UserOrderInfo>query().lambda()
                .eq(UserOrderInfo::getOutOrderNo, jdOrderId));
        if (null != userOrderInfo) {
            String n = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 24);
            // 重定向收银台地址
            String redirectUrl = String.format(JD_CASHIER_URL, userOrderInfo.getOrderId(),
                    userOrderInfo.getAmount(), Base64.encode(n), Base64.encode(String.valueOf(System.currentTimeMillis())));
            log.info("再次打开收银台，接收下单通知（跳转商户收银台） >>> 重定向收银台地址: {}", redirectUrl);
            try {
                response.sendRedirect(redirectUrl);
            } catch (IOException e) {
                // e.printStackTrace();
                log.error("再次打开收银台，接收下单通知（跳转商户收银台） >>> 重定向收银台异常, redirectUrl: {}", redirectUrl, e);
            }
            return LaoSheResponse.error();
        }
        String redirectUrl = "";
        //设置redis锁，防止未知原因造成订单重复
        final RLock lock = redissonClient.getLock("jd-order-create" + jdOrderId);
        try {
            if (!lock.tryLock(10L, TimeUnit.SECONDS)) {
                log.error("接收下单通知（跳转商户收银台） >>> 订单hold住了，稍后才能支付， jdOrderId: {}", jdOrderId);
            }
            // 生成预处理订单
            UserOrderInfo orderTemp = new UserOrderInfo();
            String orderId = UserTypeToNameUtil.setRechargeStream();
            orderTemp.setOrderId(orderId);
            // 商户订单号
            orderTemp.setOutOrderNo(jdOrderId);

            assert totalMoney != null;
            BigDecimal amount = new BigDecimal(totalMoney);

            orderTemp.setAmount(amount); // 交易金额

            orderTemp.setUserBalance(BigDecimal.ZERO);
            orderTemp.setCashBalance(BigDecimal.ZERO);

            orderTemp.setMerchantId(JD_MERCHANT_ID);
            orderTemp.setMerchantName(JD_MERCHANT_NAME);

            //orderTemp.setCardId(one.getCardId()); // 物理卡号
            orderTemp.setOrderStatus(OrderStatus.PRE_PENDING.getCode()); // 预处理

            orderTemp.setGuid(one.getGuid());
            orderTemp.setUsername(one.getUsername());
            orderTemp.setPhone(one.getPhone());
            orderTemp.setUserJobNumber(one.getUserJobNumber());

            // 外部订单时间
            // 格式"yyyy-MM-dd HH:mm:ss"，此时间将作为双方对账时间，避免商户23:59:59发送的订单请求因响应不及时被轨集记录为00:00:01的订单，导致单日对账出错
            if (StrUtil.isNotBlank(submitOrderTime)) {
                orderTemp.setOutOrderTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(submitOrderTime) * 1000), ZoneId.systemDefault()));
                log.info("外部订单时间: {}", LocalDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(submitOrderTime) * 1000), ZoneId.systemDefault()));
            }

            // 消费备注
            orderTemp.setNotes("京东-锦礼下单");
            orderTemp.setRefundAmount(BigDecimal.ZERO);

            userOrderInfoMapper.insert(orderTemp);
            String n = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 24);
            // 重定向收银台地址
            redirectUrl = String.format(JD_CASHIER_URL, orderId,
                    amount, Base64.encode(n), Base64.encode(String.valueOf(System.currentTimeMillis())));
            log.info("接收下单通知（跳转商户收银台） >>> 重定向收银台地址: {}", redirectUrl);
            response.sendRedirect(redirectUrl);
            return LaoSheResponse.success("重定向收银台成功","");
        } catch (InterruptedException | IOException e) {
            log.error("接收下单通知（跳转商户收银台） >>> 重定向收银台异常, redirectUrl: {}", redirectUrl, e);
            throw new RuntimeException(e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 收银台-立即支付
     *
     * @param dto
     * @param request
     * @param response
     * @return
     */
    @Override
    public LaoSheResponse<PayResVo> pay(JDCashierOrderPayDto dto, HttpServletRequest request, HttpServletResponse response) {


        UserOrderInfo orderInfo = userOrderInfoService.lambdaQuery().eq(UserOrderInfo::getOrderId, dto.getO())
                .eq(UserOrderInfo::getDelFlag, "0").one();
        log.info("查询订单号：{}", JSONObject.toJSONString(orderInfo));
        if (null == orderInfo) {
            return LaoSheResponse.error(CodeSet.ORDER_NOT_EXIST, null);
        }

        if (OrderStatus.TRANS_SUCCESS.getCode().equals(orderInfo.getOrderStatus())) {
            return LaoSheResponse.error(CodeSet.ORDER_STATUS_SUCCESS, "");
        }
        if (!OrderStatus.PRE_PENDING.getCode().equals(orderInfo.getOrderStatus())) {
            return LaoSheResponse.error(CodeSet.ORDER_STATUS_ERROR, "");
        }
        //加入订单锁
        final RLock lock = redissonClient.getLock("jd-order-pay" + orderInfo.getOrderId());
        final RLock accountLock = redissonClient.getLock("getAccountLock" + orderInfo.getGuid());
        try {
            if (!lock.tryLock(30L, TimeUnit.SECONDS)) {
                return LaoSheResponse.error(CodeSet.ORDER_STATUS_SUCCESS, "订单支付中，请稍后再试");
            }
            accountLock.lock();            //锁内查询用户账户余额信息
            final SysUser sysUser = sysUserService.lambdaQuery().eq(SysUser::getUserJobNumber, orderInfo.getUserJobNumber()).one();
            if (null == sysUser) {
                return LaoSheResponse.error(CodeSet.USER_NOT_EXIST, "");
            }
            if ((sysUser.getCardAccount()).add(sysUser.getCashAccount()).compareTo(orderInfo.getAmount()) < 0) {
                return LaoSheResponse.error(CodeSet.INSUFFICIENT_BALANCE, "");
            }
            //逻辑开始
            UserOrderInfo userOrderInfoU = new UserOrderInfo();
            log.info("账户表执行更新操作开始,getGuid为:{}", sysUser.getGuid());
            //1.扣除用户余额
            final boolean ifCardAccountEnough = sysUser.getCardAccount().compareTo(orderInfo.getAmount()) >= 0;
            final LocalDateTime now = LocalDateTime.now();
            BigDecimal shallDecrCardAmount = BigDecimal.ZERO;
            BigDecimal shallDecrCashAmount = BigDecimal.ZERO;
            if (ifCardAccountEnough) {    //福利卡账户余额充足
                //仅仅 扣减 福利卡 即可
                shallDecrCardAmount = orderInfo.getAmount();
                userOrderInfoU.setUserBalance(orderInfo.getAmount());
                userOrderInfoU.setCashBalance(BigDecimal.ZERO);
            } else {
                //把福利金扣完
                shallDecrCardAmount = sysUser.getCardAccount();
                //余下的金额扣现金
                shallDecrCashAmount = orderInfo.getAmount().subtract(shallDecrCardAmount);
                userOrderInfoU.setUserBalance(shallDecrCardAmount);
                userOrderInfoU.setCashBalance(shallDecrCashAmount);
            }
            sysUserService.lambdaUpdate().eq(SysUser::getGuid, sysUser.getGuid())
                    .setDecrBy(shallDecrCardAmount.compareTo(BigDecimal.ZERO) > 0, SysUser::getCardAccount, shallDecrCardAmount)
                    .setDecrBy(shallDecrCashAmount.compareTo(BigDecimal.ZERO) > 0, SysUser::getCashAccount, shallDecrCashAmount)
                    .set(SysUser::getUpdateTime, now)
                    .set(SysUser::getUpdateBy, sysUser.getGuid())
                    .update();
            log.info("账户表执行更新操作结束,userId为:{}", sysUser.getGuid());

            //2.更新订单
            userOrderInfoU.setId(orderInfo.getId());
            userOrderInfoU.setOrderStatus(OrderStatus.TRANS_SUCCESS.getCode());
            userOrderInfoU.setUpdateTime(now);
            userOrderInfoU.setWhetSync("1"); //未同步
            boolean orderUpdateRes = userOrderInfoService.updateById(userOrderInfoU);
            if (!orderUpdateRes) {
                log.warn("更新订单表失败, db更新失败, data: {}", userOrderInfoU);
            }
            //4.生成账户流水记录&支付流水记录
            //UserTransaction
            final Supplier<UserTransaction> F = () -> {
                UserTransaction userTransaction = new UserTransaction();
                //userTransaction.setTransactionId();
                userTransaction.setRechargeStream(UserTypeToNameUtil.setRechargeStream());
                //userTransaction.setThirdRechargeStream(orderInfo);
                userTransaction.setGuid(orderInfo.getGuid());
                userTransaction.setUsername(orderInfo.getUsername());
                userTransaction.setUserJobNumber(orderInfo.getUserJobNumber());
                userTransaction.setPhone(orderInfo.getPhone());
                //userTransaction.setTransactionNumber();
                //userTransaction.setCashAccount();
                //userTransaction.setCardAccount();
                //userTransaction.setAccountType();
                userTransaction.setMerchantNumber(orderInfo.getMerchantId());
                userTransaction.setTransactionType(TransactionTypeEnum.JD_CONSUMPTION.getCode());
                userTransaction.setCreateTime(now);
                userTransaction.setDelFlag("0");
                userTransaction.setTransactionTime(now);
                userTransaction.setNotes(StringUtils.hasText(orderInfo.getNotes()) ? orderInfo.getNotes() + "-京东锦礼扣款" : "京东锦礼扣款");
                userTransaction.setUserType(sysUser.getUserType());
                userTransaction.setOrderId(orderInfo.getOrderId());
                userTransaction.setOutOrderNo(orderInfo.getOutOrderNo());
                userTransaction.setTradeType(2);
                return userTransaction;
            };

            // Payment
            Supplier<UserPayment> G = () -> {
                UserPayment userPayment = new UserPayment();
                //userPayment.setPaymentId(0L);
                userPayment.setUserId(sysUser.getUserId());
                userPayment.setGuid(sysUser.getGuid());
                userPayment.setUsername(sysUser.getUsername());
                userPayment.setUserJobNumber(sysUser.getUserJobNumber());
                userPayment.setPhone(sysUser.getPhone());
                userPayment.setUserType(sysUser.getUserType());
                userPayment.setUserMealsNumber(sysUser.getUserMealsNumber());
                //userPayment.setAccountType();
                userPayment.setPaymentChannel("2");
                //userPayment.setPayNumber();
                userPayment.setTransactionTime(now);
                userPayment.setRechargeStream(UserTypeToNameUtil.setRechargeStream());
                //userPayment.setThirdRechargeStream();
                userPayment.setNotes(StringUtils.hasText(orderInfo.getNotes()) ? orderInfo.getNotes() + "-京东锦礼扣款" : "京东锦礼扣款");
                //userPayment.setCashAccount(new BigDecimal("0"));
                //userPayment.setCardAccount(new BigDecimal("0"));
                userPayment.setCreateTime(now);
                userPayment.setCreateBy(orderInfo.getGuid());
                userPayment.setDelFlag("0");
                userPayment.setOrderId(orderInfo.getOrderId());
                userPayment.setOutOrderNo(orderInfo.getOutOrderNo());
                return userPayment;
            };
            //福利账户金额足够，直接扣除即可，保存一条流水，一条支付记录
            if (ifCardAccountEnough) {
                final UserTransaction userTransaction = F.get();
                userTransaction.setTransactionNumber(orderInfo.getAmount());
                userTransaction.setCardAccount(sysUser.getCardAccount().subtract(shallDecrCardAmount));
                userTransaction.setCashAccount(sysUser.getCashAccount());
                userTransaction.setAccountType("1");
                if (userTransaction.getTransactionNumber().compareTo(BigDecimal.ZERO) > 0) {
                    userTransactionService.save(userTransaction);
                }
                final UserPayment userPayment = G.get();
                userPayment.setAccountType("1");
                userPayment.setPayNumber(orderInfo.getAmount());
                userPayment.setCardAccount(sysUser.getCardAccount().subtract(shallDecrCardAmount));
                userPayment.setCashAccount(sysUser.getCashAccount());
                if (userPayment.getPayNumber().compareTo(BigDecimal.ZERO) > 0) {
                    userPaymentService.save(userPayment);
                }

            } else {
                //福利账户金额不足 订单金额
                //保存两条流水
                //1 福利流水
                final UserTransaction userTransactionOfCard = F.get();
                userTransactionOfCard.setTransactionNumber(shallDecrCardAmount);
                userTransactionOfCard.setCardAccount(sysUser.getCardAccount().subtract(shallDecrCardAmount));
                userTransactionOfCard.setCashAccount(sysUser.getCashAccount());
                userTransactionOfCard.setAccountType("1");
                if (userTransactionOfCard.getTransactionNumber().compareTo(BigDecimal.ZERO) > 0) {
                    userTransactionService.save(userTransactionOfCard);
                }
                //2 现金流水
                final UserTransaction userTransactionOfCash = F.get();
                userTransactionOfCash.setTransactionNumber(shallDecrCashAmount);
                userTransactionOfCash.setCardAccount(sysUser.getCardAccount().subtract(shallDecrCardAmount));
                userTransactionOfCash.setCashAccount(sysUser.getCashAccount().subtract(shallDecrCashAmount));
                userTransactionOfCash.setAccountType("2");
                if (userTransactionOfCash.getTransactionNumber().compareTo(BigDecimal.ZERO) > 0) {
                    userTransactionService.save(userTransactionOfCash);
                }


                //福利支付
                final UserPayment userPaymentOfCard = G.get();
                userPaymentOfCard.setPayNumber(shallDecrCardAmount);
                userPaymentOfCard.setCardAccount(sysUser.getCardAccount().subtract(shallDecrCardAmount));
                userPaymentOfCard.setCashAccount(sysUser.getCashAccount());
                userPaymentOfCard.setAccountType("1");
                if (userPaymentOfCard.getPayNumber().compareTo(BigDecimal.ZERO) > 0) {
                    userPaymentService.save(userPaymentOfCard);
                }
                //现金支付
                final UserPayment userPaymentOfCash = G.get();
                userPaymentOfCash.setPayNumber(shallDecrCashAmount);
                userPaymentOfCash.setCardAccount(sysUser.getCardAccount().subtract(shallDecrCardAmount));
                userPaymentOfCash.setCashAccount(sysUser.getCashAccount().subtract(shallDecrCashAmount));
                userPaymentOfCash.setAccountType("2");
                if (userPaymentOfCash.getPayNumber().compareTo(BigDecimal.ZERO) > 0) {
                    userPaymentService.save(userPaymentOfCash);
                }
            }
            try {
                //同步订单状态给到京东  测试发现，accessKey
                Boolean res = payConfirmToJd(PayConfirmParamUtil.getPayConfirmParam(RSA_SIGN_PRIVATE_KEY, RSA_ENCRYPT_PUBLIC_KEY, ACCESS_KEY,
                        orderInfo.getOutOrderNo()));
                if (res) {   //更新订单状态
                    final UserOrderInfo userOrderInfo = new UserOrderInfo();
                    userOrderInfo.setId(orderInfo.getId());
                    userOrderInfo.setWhetSync("2"); //已同步
                    userOrderInfo.setUpdateTime(now);
                    userOrderInfoService.updateById(userOrderInfo);
                }
            } catch (Exception e) {
                log.error("同步订单到京东出错:", e);
            }
        } catch (Exception e) {
            log.error("jd支付报错：", e);
            return LaoSheResponse.error(CodeSet.SYSTEM_EXCEPTION, "");
        } finally {
            if (accountLock.isLocked() && accountLock.isHeldByCurrentThread()) {
                accountLock.unlock();
            }
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }

        PayResVo payResVo = new PayResVo();
        payResVo.setAmount(orderInfo.getAmount());
        payResVo.setUid(orderInfo.getUserJobNumber());
        return LaoSheResponse.successData(payResVo, "");
    }

    private Boolean payConfirmToJd(PayConfirmParam payConfirmParam) {

        //JD_confirm_URL
        Map<String, String> map;
        map = JSONObject.parseObject(JSONObject.toJSONString(payConfirmParam), Map.class);
        String jdRes = HttpUtils.sendFormPost(JD_confirm_URL, null, map);
        log.info("调用京东确认接口返回报文：{}", jdRes);
        JdResultParam JdResultParam = JSONObject.parseObject(jdRes, JdResultParam.class);
        //重复确认
        return "0003".equals(JdResultParam.getResultCode())   //确认成功
                || "3103".equals(JdResultParam.getResultCode());

    }

}